本文为《蚂蚁金服 Service Mesh 大规模落地系列》第二篇,该系列将会从核心、RPC、消息、无线网关、控制面、安全、运维、测试等模块对 Service Mesh 双十一大规模落地实践进行详细解析。文末包含往期系列文章。
Service Mesh 作为蚂蚁金服向下一代云原生架构演进的核心基础设施,在2019年得到了大规模的应用与落地,截止目前,蚂蚁金服的 Service Mesh 数据平面 MOSN 已接入应用数百个,接入容器数量达数十万,一举成为全世界最大的 Service Mesh 集群。同时,在刚刚结束的双十一大促中,Service Mesh 的表现也十分亮眼,RPC 峰值 QPS 达到了几千万,消息峰值 TPS 达到了几百万,且引入 Service Mesh 后的平均 RT 增长幅度控制在 0.2 ms 以内。本文为整个 Service Mesh 系列文章的消息篇,作者:刘翔(花名:无勤),蚂蚁金服消息 Mesh 负责人, 消息中间件核心研发成员,专注于高吞吐、低延迟的消息中间件研发,以及云原生时代下一代消息系统的架构设计与研发。本文将从以下几个方面对消息 Mesh 进行解读:- 对消息 Mesh 进行介绍,解答消息 Mesh 在整个 Service Mesh 中的地位是什么,它又能为业务带来哪些价值;
- 结合蚂蚁金服消息中间件团队 Mesh 化的实践与思考,阐述如何在消息领域进行 Mesh 化改造;
- 消息 Mesh 在蚂蚁金服内部大规模落地过程中遇到的问题与挑战,以及对应的解决方案;
- 消息流量精细化调度上的思考与在 Mesh 上的实现与落地;
Service Mesh 作为云原生场景下微服务架构的基础设施(轻量级的网络代理),正受到越来越多的关注。Service Mesh 不仅负责在微服务架构的复杂拓扑中可靠地传递请求,也将限流、熔断、监控、链路追踪、服务发现、负载均衡、异常处理等与业务逻辑无关的流量控制或服务治理行为下沉,让应用程序能更好地关注自身业务逻辑。微服务架构中的通信模式实际上是多种多样的,既包含同步的请求调用,也包含异步的消息/事件驱动,然而流行的 Service Mesh 实现(Istio,Linkerd,Consul Connect等),都仍局限在对微服务中同步请求调用的关注,却无法管理和追踪异步消息流量。而消息 Mesh 则是对这一块的重要补充,通过将消息 Mesh 有机地融合到 Service Mesh 中,可以帮助 Service Mesh 实现对所有微服务流量的管控和追踪,从而进一步完善其架构目标。在传统的消息中间件领域,我们更关注的是消息服务端的性能、服务可用性、数据可靠性等核心指标,而与业务应用密切相关的一些能力,包括消息的流量控制(限流、熔断、灰度、着色、分组等),消息的服务治理(消息量级与消息应用拓扑等),消息链路的追踪(消息轨迹)却往往不尽如人意。这不仅是因为传统模式下上述能力的提供和优化都涉及客户端的改造与大规模升级,整个过程常常比较漫长,难以快速根据有效反馈不断优化,更重要的是缺乏一个统一的架构指导思想,混乱无序地向客户端叠加相关功能只会让消息客户端变得越来越臃肿和难以维护,也变向增加了业务系统的接入、调试和排查问题的成本。而消息 Mesh 作为 Service Mesh 的补充,能显著带来如下价值和收益:- 快速升级 - 通过将与业务逻辑无关的一些核心关键能力下沉到 Sidecar 中,使这些能力的单独快速迭代与升级成为可能;
- 流量控制 - 可以向 Sidecar 中集成各种流量控制策略,例如可根据消息类型、消息数量、消息大小等多种参数来控制消息的发送和消费速率;
- 流量调度 - 通过打通 Sidecar 节点之间的通信链路,可以利用 Sidecar 的流量转发来实现任意精度的消息流量调度,帮助基于事件驱动的微服务应用进行多版本流量管理、流量着色、分组路由、细粒度的流量灰度与 A/B 策略等;
- 消息验证 - 消息验证在基于事件驱动的微服务架构中正变得越来越重要,通过将这部分能力下沉到 Sidecar,不仅可以让业务系统无缝集成消息验证能力,也可以让 Sidecar 通过 Schema 理解消息内容,并进一步具备恶意内容识别等安全管控能力;
- 可观测性 - 由于所有的消息流量都必须通过 Sidecar,因此可以为 Sidecar 上的消息流量按需增加 Trace 日志,Metrics 采集,消息轨迹数据采集等能力,并借此进一步丰富消息服务的治理能力;
在蚂蚁金服内部,Msgbroker 基于推模型提供高可靠、高实时、事务消息、header 订阅等特性,帮助核心链路进行异步解耦,提升业务的可扩展能力,并先后伴随蚂蚁金服众多核心系统一起经历了分布式改造、单元化改造与弹性改造,目前主要承载蚂蚁金服内部交易、账务、会员、消费记录等核心在线业务的异步消息流量。由于 Service Mesh 的推进目标也是优先覆盖交易支付等核心链路,为在线业务赋能,因此我们优先选择对 Msgbroker 系统进行 Mesh 化改造。下面将以 Msgbroker 为例,重点剖析 Mesh 化后在整体架构和核心交互流程上的变化,为消息领域的 Mesh 化改造提供参考。消息 Mesh 化后的整体架构如上图所示,与原有的消息架构相比,主要的变化有:- 客户端不再与服务端直连,而是通过 Sidecar 进行请求的中转,对客户端而言,Sidecar 实际上是它唯一能感知到的消息服务端,对服务端而言,Sidecar 则扮演着客户端的角色;
- 所有 Sidecar 都会与控制平面交互,接收服务端地址列表、流量管控和调度配置、运行时动态配置等的下发,从而使数据平面具备限流、熔断、异常重试、服务发现、负载均衡、精细化流量调度等能力;
当 Sidecar 代理了消息客户端的所有请求后,一旦 Sidecar 完成消息服务的发现与服务端/客户端路由数据的缓存,无论是客户端的发消息请求还是服务端的推消息请求,都能由 Sidecar 进行正确的代理转发,而这一切的关键,则是 Sidecar 与消息客户端协同完成一系列的初始化操作。消息 Mesh 化后具体的初始化流程如下所示,与原有的初始化流程相对比,主要有如下不同:- 在经过 Mesh 化改造后,消息客户端不再直接向 SOFARegistry 订阅消息服务端的地址,而是将所有消息元数据(包含业务应用声明的消息 Topic、发送/订阅组 GroupId 等关键信息)通过 HTTP 请求上报给 MOSN,由 MOSN 进行元数据的持久化(用于 MOSN 异常 Crash 后的恢复)以及消息服务端地址的订阅和处理;
- 当客户端收到 MOSN 对于注册请求的响应时,会主动与 MOSN 建立连接,并将与该连接相关的 Group 集合信息通过控制指令发送给 MOSN,由于客户端与 MOSN 可能存在多个连接,且不同连接上的 Group 集合可以不同,而 MOSN 与同一个消息服务端只维持一个连接,因此控制指令无法向消息数据一样直接进行转发,而是需要汇总计算所有 GroupId 集合后再统一广播给消息服务端集群。由于上述计算逻辑十分复杂,需要包含过滤和聚合,且存在动态和并发行为,一旦因计算错误则会严重影响到消息投递的正确性,因此当前 MOSN 绕过了该指令的代理,只利用客户端的控制指令进行相关数据的校验,以及更新客户端连接的映射信息(用于 MOSN 的客户端连接选择),而是选择依赖消息客户端的改造引入上述 HTTP 注册请求来构造全量控制指令;
MOSN:https://github.com/sofastack/sofa-mosn消息中间件最关键的挑战,在于如何在洪峰流量下依然保障消息服务的高可靠与高实时。而在消息 Mesh 化的大规模实施过程中,还需要考虑数十万的容器节点对数据面整体稳定性和控制面性能带来的巨大挑战,这些都依赖于完善的 Sidecar 运维体系,包括 Sidecar 的资源分配策略,以及 Sidecar 独立变更升级的策略。当 Service Mesh 的实体 MOSN 作为 Sidecar 与业务容器部署在一起时,就不再像消息服务端一样只需要关心自身的资源消耗,而是必须精细化其 CPU、内存等资源的分配,才能达到与应用最优的协同合作方式。在 Sidecar 的精细化资源管理上,先后经历了独立分配、通过超卖与业务容器共享、细粒度的 CPU 资源分配策略和内存 OOM 策略调整等多个阶段,最终基于 Service Mesh 将原有的与业务无关的逻辑下沉到 Sidecar,其占用的资源实际是原来业务容器会使用的资源这一假设,在零新增成本的情况下平稳支撑了数十万规模级别的 Sidecar 容器分配。关于资源管理更详细的内容可以期待后续 Service Mesh 系列文章中的运维篇,本文就不再赘述。为了达到 Sidecar 这一类基础设施的变更升级对业务完全无感知的目的,就需要使 MOSN 具备平滑升级的能力。在平滑升级过程中,新的 MOSN 首先会被注入,并通过共享卷的 UnixSocket 去检查是否存在老的 MOSN,若存在,则利用内核 Socket 的迁移实现老 MOSN 的连接全部迁移给新 MOSN,如下图所示,最终再让老 MOSN 优雅退出,从而实现 MOSN 在整个升级和发布过程中对业务无任何打扰,关于 MOSN 本身平滑升级更多的内容,可以参考 Service Mesh 系列文章中的核心篇。上述平滑升级方案,其实隐含了一个非常重要的前提,单条连接上的请求必须是单向的。从下图可知,对于 RPC 场景,其单条连接的角色是固定的,只能是服务端连接或客户端连接,且对一次请求的代理过程也是固定的,总是从服务端连接上收到一个请求,再从客户端连接将请求转发出去,因此 RPC 可以直接使用上述平滑升级方案。然而,在消息场景特别是 Msgbroker 场景下,如下图所示,MOSN 上的连接请求实际上是双向的,不仅客户端会使用该连接进行消息的发送,服务端也会利用该连接将消息主动推送给 MOSN,这就会给上述连接迁移带来新的问题和挑战:- 在连接迁移的过程中,如果消息客户端已处理完经过 MOSN 转发的服务端投递消息请求,但是还未回复响应,此时若把连接迁移到新的 MOSN,则新的 MOSN 将收到上述响应,但由于新 MOSN 缺失上下文,无法将该响应返回给正确的消息服务端;
- 在连接迁移完成,但老 MOSN 还未优雅退出期间,由于两个 MOSN 与消息服务端都存在连接,两者都会收到服务端发送的投递消息请求,但因两个 MOSN 与服务端连接的状态各自独立,可能会使客户端收到的请求ID冲突;
解决上述问题的思路其实很简单,即为在平滑升级的过程中,禁止服务端向老 MOSN 发送投递消息请求,保证即使在消息场景整个平滑升级过程中的所有连接仍然是单工通信的。具体对平滑升级流程的改动如下:- 老 MOSN 平滑升级指令后,会立即向所有的消息服务端发送禁止再接收消息的控制指令;
- 新 MOSN 感知老 MOSN 完成前置操作,开始进行原有的平滑升级流程,进行初始化和存量连接迁移;
- 新 MOSN 完成存量连接迁移后,向所有的消息服务端发送接收消息的控制指令,开始正常的消息订阅;
首先,控制平面会将与流量调度相关的规则下发至 MOSN,规则主要包含该应用下所有容器节点的 IP 地址与流量权重,这是能够进行精细化流量调度的前提。其次,当 MOSN 收到消息投递请求时,会判断请求的来源,若请求来源于其他 MOSN 节点,则会直接将该请求转发给客户端,避免消息投递请求的循环转发,而若请求来源于消息服务端,则 MOSN 会根据自身的流量权重来决定下一步的路由,若自身的流量权重是100%,会同样将该请求转发给客户端,但若自身权重小于100%,则会按照配置的权重将剩余请求均匀转发给其他流量权重为100%的 MOSN 节点。最后,与 RPC 的点对点通信方式不同,无论是消息发送端还是订阅端都只与消息服务端通信,这意味着即使进行了消息 Mesh 化改造后,MOSN 也只与消息服务端通信,同一个应用的 MOSN 节点之间是不存在消息连接的,为了实现 MOSN 之间的消息流量转发,则需要内置实现一个与业务应用进程同生命周期的消息转发服务,由同应用内的所有其他 MOSN 节点订阅并在需要转发时调用。消息 Mesh 经过蚂蚁消息中间件团队大半年的打磨和沉淀,已经迈出了坚实的一大步:在开源社区迟迟未在消息 Mesh 上取得实质性进展时,我们已经为蚂蚁内部主流消息中间件打通了数据平面。
同时也有充满想象力的一小步:依赖消息的精细化流量调度,预期可以发挥更大的业务价值,包括基于事件驱动的微服务应用多版本流量管理、流量着色、分组路由、细粒度的流量灰度与A/B策略等,等待着去开发与挖掘。
这些都在双十一大促中取得了不俗的成绩。未来,我们将会持续加大对消息 Mesh的投入,为消息 Mesh 支持更多的消息协议,赋予更多开箱即用的的消息流量管控和治理能力,并进一步结合 Knative 探索消息精细化流量调度在 Serverless 下的应用场景。最后,也欢迎志同道合的伙伴加入我们(看次条),一起参与金融级分布式消息系统、云原生时代的下一代消息系统的架构设计和研发。